﻿using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using DotNetStar.TheKing.Infrastructure;
using Newtonsoft.Json;
using SDDP;
namespace DotNetStar.TheKing.Domain
{
    /// <summary>
    /// 用户
    /// </summary>
    [Table("Users")]
    public class User : IEntity<long>
    {
        #region 属性
        /// <summary>
        /// Id
        /// </summary>
        [Key,DatabaseGenerated(DatabaseGeneratedOption.Identity)]
        public long Id { get; set; }

        /// <summary>
        /// 昵称
        /// </summary>
        [Display(Name ="昵称")]
        [Required(ErrorMessage ="{0}不能为空"), StringLength(30,ErrorMessage ="{0}不能超过{1}个字")]
        public string UserName { get; set; }

        /// <summary>
        /// 邮箱
        /// </summary>
        [Display(Name = "邮箱")]
        [StringLength(300, ErrorMessage = "{0}不能超过{1}个字符")]
        [DataType(DataType.EmailAddress,ErrorMessage ="{0}格式不正确")]
        public string Email { get; set; }

        /// <summary>
        /// 邮箱是否验证。
        /// </summary>
        public bool EmailConfirmed { get; set; }

        /// <summary>
        /// 手机号。
        /// </summary>
        [Display(Name = "手机")]
        [StringLength(16, ErrorMessage = "{0}不能超过{1}个字符")]
        [RegularExpression(Const.PATTERN_CHINA_MOBILE,ErrorMessage ="{0}格式不正确")]
        public string Mobile { get; set; }

        /// <summary>
        /// 手机是否验证。
        /// </summary>
        public bool MobileConfirmed { get; private set; }

        /// <summary>
        /// 密码
        /// </summary>
        [StringLength(128)]
        public string Password { get; private set; }

        /// <summary>
        /// 密码相关的随机码。
        /// </summary>
        [StringLength(128)]
        public string PasswordSalt { get;private set; }

        /// <summary>
        /// 安全令牌
        /// </summary>
        [ConcurrencyCheck]
        public string SecurityToken { get; private set; }

        /// <summary>
        /// 注册时间。
        /// </summary>
        public DateTime CreatedTime { get; private set; } = DateTime.Now;

        /// <summary>
        /// 最后一次登录时间
        /// </summary>
        public DateTime LastLoginTime { get; private set; }

        /// <summary>
        /// 状态
        /// </summary>
        public UserStatus Status { get; private set; }

        /// <summary>
        /// 头像url
        /// </summary>
        [StringLength(150)]
        public string AvatarUrl { get; set; } = "/images/default-avatar.png";

        /// <summary>
        /// 交易积分
        /// </summary>
        public int TradingPoints { get; private set; }

        /// <summary>
        /// 等级
        /// </summary>
        public int Level { get; private set; } = 1;

        /// <summary>
        /// 当前经验值
        /// </summary>
        public long ExpPoints { get;private set; }
        /// <summary>
        /// 升级经验值
        /// </summary>
        public long ExpMaxPoints { get; private set; }

        public Roles Role { get;private set; }

        /// <summary>
        /// 性别
        /// </summary>
        public Gender? Gender { get; set; }
        /// <summary>
        /// 技术经验（年）
        /// </summary>
        public int? TechExpYear { get; set; }

        /// <summary>
        /// 个性签名
        /// </summary>
        [StringLength(255)]
        public string Signature { get; set; }

        /// <summary>
        /// 出生年月
        /// </summary>
        public DateTime? Birth { get; set; }

        /// <summary>
        /// 其他可序列化的个人信息
        /// </summary>        
        public string Information { get;private set; }

        /// <summary>
        /// 邀请花费（金币）
        /// <para>负数不允许被邀请</para>
        /// </summary>
        
        public int? InviteCost { get;private set; }
        #endregion

        #region 领域方法

        /// <summary>
        /// 设置密码。
        /// </summary>
        /// <param name="password">加密了的密码。</param>
        /// <param name="passwordSalt">密码相关的随机码</param>
        public void SetPassword(string password,string passwordSalt)
        {
            this.Password = password;
            this.PasswordSalt = passwordSalt;
        }

        /// <summary>
        /// 生成随机码。
        /// </summary>
        public void GenerateSecurityToken() => this.SecurityToken = Guid.NewGuid().ToString().Replace("-", string.Empty);

        /// <summary>
        /// 激活邮箱。
        /// </summary>
        public void ConfirmEmail() => this.EmailConfirmed = true;
        /// <summary>
        /// 激活手机号。
        /// </summary>
        public void ConfirmMobile() => this.MobileConfirmed = true;

        /// <summary>
        /// 设置最后一次登录时间。
        /// </summary>
        public void SetLastLoginTime() => this.LastLoginTime = DateTime.Now;

        /// <summary>
        /// 禁止访问
        /// </summary>
        public void Forbid() => this.Status = UserStatus.Forbidden;

        /// <summary>
        /// 正常
        /// </summary>
        public void Normal() => this.Status = UserStatus.Normal;
        /// <summary>
        /// 待审核
        /// </summary>
        public void Pending() => this.Status = UserStatus.Pending;
        
        /// <summary>
        /// 增加经验值。如果达到最大经验，则触发升级。
        /// </summary>
        /// <param name="exp">要增加的经验值。</param>
        public void IncreaseExp(int exp)
        {
            var newExp = this.ExpPoints + exp;
            if (newExp >= this.ExpMaxPoints)//升级
            {
                this.ExpPoints = newExp - this.ExpMaxPoints;
                this.IncreaseLevel();
                CalculateMaxExp();                
            }
            else
            {
                this.ExpPoints = newExp;
            }
        }

        /// <summary>
        /// 计算经验最大值。
        /// </summary>
        /// <param name="level"></param>
        public void CalculateMaxExp()
        {
            this.ExpMaxPoints = (long)(Math.Pow((this.Level - 1), 3)+10) / 5 * ((this.Level - 1) * 2 + 50);
        }

        /// <summary>
        /// 增加交易积分
        /// </summary>
        /// <param name="tradingPoints">要增加的交易积分，负数为减少。</param>
        public void IncreaseTradingPoint(int tradingPoints) => this.TradingPoints += tradingPoints;

        /// <summary>
        /// 增加等级
        /// </summary>
        /// <param name="step">偏移量</param>
        public void IncreaseLevel(int step = 1) => this.Level += step;

        /// <summary>
        /// 更换角色
        /// </summary>
        /// <param name="role"></param>
        public void ChangeRole(Roles role) => this.Role = role;

        /// <summary>
        /// 初始化角色
        /// </summary>
        public void InitialRole() => this.Role = Roles.User;

        /// <summary>
        /// 获取年龄，没写生日，返回0;
        /// </summary>
        /// <returns></returns>
        public int GetAge()
        {
            if (this.Birth.HasValue)
            {
                return DateTime.Now.Year - this.Birth.Value.Year;
            }
            return 0;
        }

        /// <summary>
        /// 不允许被邀请
        /// </summary>
        public void NotAllowInvitation() => this.InviteCost = -1;

        /// <summary>
        /// 设置允许被邀请的身价。
        /// </summary>
        /// <param name="cost">null 表示免费，否则为正数</param>
        /// <exception cref="ArgumentOutOfRangeException">身价不允许是负数。</exception>
        public void SetAllowInvitationCost(int? cost)
        {
            if(cost.HasValue && cost.Value < 0)
            {
                throw new ArgumentOutOfRangeException("身价不允许为负数");
            }
            this.InviteCost = cost;
        }

        public UserInfomation GetInformation()
        {
            if (this.Information.IsNullOrWhiteSpace())
            {
                return UserInfomation.Empty;
            }
            return JsonConvert.DeserializeObject<UserInfomation>(this.Information);
        }

        public void SetUserInformation(UserInfomation infomation)
            => this.Information = JsonConvert.SerializeObject(infomation);

        /// <summary>
        /// 不允许被邀请
        /// </summary>
        public bool NoInvite => this.InviteCost.HasValue  && this.InviteCost.Value < 0;
        #endregion

        #region 导航属性
        [NotMapped]
        public IEnumerable<Content> Contents { get; set; } = new HashSet<Content>();
        [NotMapped]
        public IEnumerable<ContentComment> ContentComments { get; set; } = new HashSet<ContentComment>();
        #endregion
    }

    [Serializable,NotMapped]
    public class UserInfomation
    {
        public static UserInfomation Empty => new UserInfomation();
    }
}
